/*
 * 1.00 2012/10/18
 *
 * Copyright (c) 2012 Motch_bs
 * All rights reserved.
 *
 * このソフトウェアは，Motch_bsに所有される機密情報である。
 * これらの機密情報を漏洩してはならず，Motch_bs の意図する
 * 認可の元においてこれを使用すべし。
 */
package jp.co.ufp.voicetch.base.audio;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.util.Log;
import edu.emory.mathcs.jtransforms.fft.DoubleFFT_1D;

/**
 * 
 * 録音をする
 * 
 * @author Motch_bs
 * 
 */
public class Recorder {

    /** 録音制御フラグ */
    public volatile boolean isRecording = false;

    /** 保存ファイル */
    private File file;

    /** 保存ファイル(ピッチシフト) */
    private File filePitchShift;

    /** Wav変換後保存ファイル */
    private File wavFile;

    /** 周波数 */
    private int frequency = 8000;

    /** チャンネル設定 */
    private int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;

    /** エンコーディング */
    private int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;

    /** バッファサイズ */
    private int defaultBufferSize = 640;

    /** 変換クラス */
    private Converter converter = new Converter();

    /**
     * コンストラクタ
     * 
     * @param file
     */
    public Recorder(File pFile) {
	super();
	this.file = pFile;
    }

    /**
     * PitchShift用ファイルの取得
     * 
     * @return filePitchShift
     */
    public File getFilePitchShift() {
	return filePitchShift;
    }

    /**
     * PitchShift用ファイルの設定
     * 
     * @param filePitchShift
     */
    public void setFilePitchShift(
	File filePitchShift) {
	this.filePitchShift = filePitchShift;
    }

    /**
     * Wavファイルの取得
     * 
     * @return wavFile
     */
    public File getWavFile() {
	return wavFile;
    }

    /**
     * Wavファイルの設定
     * 
     * @param wavFile
     */
    public void setWavFile(
	File wavFile) {
	this.wavFile = wavFile;
    }

    /**
     * 周波数の取得
     * 
     * @return frequency
     */
    public int getFrequency() {
	return frequency;
    }

    /**
     * 周波数の設定
     * 
     * @param frequency
     */
    public void setFrequency(
	int frequency) {
	this.frequency = frequency;
    }

    /**
     * チャンネルの取得
     * 
     * @return channelConfiguration
     */
    public int getChannelConfiguration() {
	return channelConfiguration;
    }

    /**
     * チャンネルの設定
     * 
     * @param channelConfiguration
     */
    public void setChannelConfiguration(
	int channelConfiguration) {
	this.channelConfiguration = channelConfiguration;
    }

    /**
     * エンコーディングの取得
     * 
     * @return audioEncoding
     */
    public int getAudioEncoding() {
	return audioEncoding;
    }

    /**
     * エンコーディングの設定
     * 
     * @param audioEncoding
     */
    public void setAudioEncoding(
	int audioEncoding) {
	this.audioEncoding = audioEncoding;
    }

    /**
     * バッファサイズを取得
     * 
     * @return defaultBufferSize
     */
    public int getDefaultBufferSize() {
	return defaultBufferSize;
    }

    /**
     * バッファサイズを設定
     * 
     * @param defaultBufferSize
     */
    public void setDefaultBufferSize(
	int defaultBufferSize) {
	this.defaultBufferSize = defaultBufferSize;
    }

    /**
     * 録音する
     */
    public void record() {
	// 保存ファイル
	// 録音前にデータを消す
	if (file.exists()) {
	    file.delete();
	}
	// 空ファイルの作成
	try {
	    file.createNewFile();
	}
	catch (IOException e) {
	    throw new IllegalStateException("Failed to create " + file.toString());
	}

	try {
	    // 保存ファイルへの書込みストリームを生成
	    OutputStream os = new FileOutputStream(file);
	    BufferedOutputStream bos = new BufferedOutputStream(os);
	    DataOutputStream dos = new DataOutputStream(bos);
	    // AudioRecordオブジェクトの作成
	    int bufferSize = AudioRecord.getMinBufferSize(
		frequency, channelConfiguration, audioEncoding);
	    // バッファサイズを確保できた場合
	    if (bufferSize <= 0) {
		bufferSize = defaultBufferSize;
	    }
	    AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
		    frequency, channelConfiguration,
		    audioEncoding, bufferSize);

	    short[] buffer = new short[bufferSize];
	    // 録音の開始
	    audioRecord.startRecording();
	    // フラグが立っている間は録音し続ける。
	    while (isRecording) {
		int bufferReadResult = audioRecord.read(buffer, 0, bufferSize);
		for (int i = 0; i < bufferReadResult; i++) {
		    dos.writeShort(buffer[i]);
		}

	    }
	    // 録音を停止する
	    audioRecord.stop();
	    dos.close();
	    audioRecord.release();
	}
	catch (FileNotFoundException e) {
	    e.printStackTrace();
	    Log.e("AudioTrack", "Record Failed:FileNotFoundException");
	}
	catch (IOException e) {
	    e.printStackTrace();
	    Log.e("AudioTrack", "Record Failed:IOException");
	}
    }

    /**
     * ピッチシフトして保存
     */
    public void convertPitchShift() {
	try {
	    // ---------------------------------------------------------
	    // 保存ファイルへの書込みストリームを生成
	    OutputStream os = new FileOutputStream(filePitchShift);
	    BufferedOutputStream bos = new BufferedOutputStream(os);
	    DataOutputStream dos = new DataOutputStream(bos);
	    // ---------------------------------------------------------
	    // 入力ファイルへの書込みストリームを生成
	    InputStream is = new FileInputStream(file);
	    BufferedInputStream bis = new BufferedInputStream(is);
	    DataInputStream dis = new DataInputStream(bis);
	    // ---------------------------------------------------------
	    // AudioRecordオブジェクトの作成
	    int bufferSize = (int) file.length();
	    //
	    int count = 0;
	    try {
		List<Double> list = new ArrayList<Double>();
		double[] data = new double[bufferSize];
		try {
		    while (true) {
			double d = dis.readDouble();
			data[count] = d;
			list.add(Double.valueOf(d));
			count++;
		    }
		}
		catch (EOFException e) {
		    Log.d("convertPitchShift", "count=" + count);
		}
		finally {
		    dis.close();
		}

		// // ピッチシフト
		// PitchShift pitchShift = new PitchShift(data.length);
		// pitchShift.setSampleRate(frequency);
		// double[] d_data = pitchShift.smbPitchShift(data, 0,
		// data.length);

		// int FFT_SIZE = 8 ;
		// double[] data ;
		// data = new double[FFT_SIZE];
		//
		DoubleFFT_1D fft = new DoubleFFT_1D(bufferSize);

		// フーリエ変換(FFT)の実行
		fft.realForward(data);
		// data[0]は実数成分、data[1]は虚数成分～data[n]は実数成分、data[n+1}は虚数成分
		for (int i = 0; i < bufferSize; i++)
		{
		    // フーリエ変換後のデータを出力
		    //Log.d("FFT", "i=" + i + " val=" + data[i]);
		}

		// 逆フーリエ変換
		fft.realInverse(data, true);
		for (int i = 0; i < bufferSize; i++)
		{
		    // 逆フーリエ変換後のデータを出力
		    //Log.d("IFFT", "i=" + i + " val=" + data[i]);
		}

		for (int i = 0; i < data.length; i++) {
		    dos.writeDouble(data[i]);
		}
		dos.close();
		Log.i("convertPitchShift", "convert end.");
	    }
	    catch (IOException e) {
		e.printStackTrace();
	    }
	}
	catch (FileNotFoundException e) {
	    e.printStackTrace();
	    Log.e("AudioTrack", "Record Failed:FileNotFoundException");
	}
    }

    /**
     * WAVヘッダをつけて保存。各種例外処理略 チェック処理略
     * 
     */
    public void convertWavFile() {
	try {
	    FileInputStream in = new FileInputStream(file);
	    FileOutputStream out = new FileOutputStream(wavFile);

	    // ヘッダ作成 サンプルレート8kHz
	    byte[] header = converter.createWavHeader(frequency, (int) file.length());
	    // ヘッダの書き出し
	    out.write(header);

	    // 録音したファイルのバイトデータ読み込み
	    int n = 0, offset = 0;
	    byte[] buffer = new byte[(int) file.length()];
	    while (offset < buffer.length
		    && (n = in.read(buffer, offset, buffer.length - offset)) >= 0) {
		offset += n;
	    }
	    // バイトデータ書き込み
	    out.write(buffer);

	    // 終了
	    in.close();
	    out.close();
	}
	catch (FileNotFoundException e) {
	    e.printStackTrace();
	}
	catch (IOException e) {
	    e.printStackTrace();
	}

    }
}
